Skip to content

Use fast invoke js extension methods in sync calls (#9917)#12426

Open
msynk wants to merge 21 commits into
bitfoundation:developfrom
msynk:9917-blazorui-fastinvoke-js
Open

Use fast invoke js extension methods in sync calls (#9917)#12426
msynk wants to merge 21 commits into
bitfoundation:developfrom
msynk:9917-blazorui-fastinvoke-js

Conversation

@msynk

@msynk msynk commented Jun 7, 2026

Copy link
Copy Markdown
Member

closes #9917

Summary by CodeRabbit

  • New Features
    • Added a helper to scroll elements into view (replacing the prior option-based approach).
  • Bug Fixes
    • Improved JavaScript interop reliability via faster-path calls, safer null handling, and better error reporting to reduce render/update glitches.
    • Added defensive guards to sizing/geometry calculations and clamping logic; components now skip updates when measurements are unavailable.
    • Improved disposal/teardown robustness so cleanup won’t throw during lifecycle changes.
    • Hardened chart rendering/callback behavior and data-grid scrollIntoView safety.
  • Chores
    • Expanded interop-focused test coverage to validate fast sync/async behavior and contracts.

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Migrates many JS interop call sites to FastInvoke, centralizes fast-path error reporting and runtime validity checks, hardens TypeScript/DOM interactions, refactors Extras resource loading, adjusts component null-handling and lifecycle disposal, and adds scanner and runtime contract tests.

Changes

FastInvoke JS Interop Migration and Resilience

Layer / File(s) Summary
Core FastInvoke runtime behavior and reflection caches
src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs, src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs, src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/JsInteropConstants.cs
Adds public OnError callback and private ReportError, centralizes fast-path error routing for both JsonException and JSException, converts timeout overloads to async to preserve CancellationTokenSource lifetime, caches reflected runtime members for IsRuntimeInvalid, and introduces JsInteropConstants with DynamicallyAccessedMemberTypes for trimming.
C# interop wrapper migration to FastInvoke
src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/*, src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/*, src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/*, src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/*, src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/*, src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/*, src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/*, src/BlazorUI/Bit.BlazorUI/Components/Inputs/*, src/BlazorUI/Bit.BlazorUI/Components/Lists/*, src/BlazorUI/Bit.BlazorUI/Components/Surfaces/*, src/BlazorUI/Bit.BlazorUI/Components/Utilities/*, src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/*
Many extension methods switch from Invoke/InvokeVoid to FastInvoke/FastInvokeVoid. Several return types become nullable where fast-path may yield null; inline comments clarify when async invocation must remain (e.g., PdfReader render/refresh operations).
Extras per-URL script & stylesheet loader
src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts, src/BlazorUI/Bit.BlazorUI.Extras/Services/BitMarkdownService.cs
Replaces prior aggregation via scripts.join('|') substring-matching with per-URL promise caches, URL normalization against document.baseURI, host-resource verification, dynamic injection, and failed-cache eviction for retries. New scrollElementIntoView replaces scrollOptionIntoView. Markdown service updates conditional to use is not true.
TypeScript DOM guards and utils
src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts, src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts, src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts, src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.ts, src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts, src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.ts, src/BlazorUI/Bit.BlazorUI/Scripts/Swipes.ts, src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts, src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts, src/BlazorUI/Bit.BlazorUI/Scripts/PageVisibility.ts
Adds defensive checks for missing/falsy elements, optional chaining for method calls, try/catch around DOM reads/writes and disposals, safer defaults (?:, ??), safer array lookups via find instead of [0], and updates getBoundingClientRect to return DOMRect | null instead of empty object. PageVisibility adds stored callback and dispose() method.
Component null handling & lifecycle disposal
src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Utilities/Overlay/BitOverlay.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.razor.cs, src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.razor.cs, src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs
Coalesces nullable JS results with null-coalescing (?? 0 or ?? string.Empty), early-returns when geometry is null, persists and safely disposes DotNetObjectReference fields across component lifetime, and BitPageVisibility now implements IDisposable.
Splitter nullable dimension handling
src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts, src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs, src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs
Splitter JS interop wrappers switch to FastInvoke, getter methods return nullable types (double?), TS guards and try/catch added for width/height reads/writes, and drag calculations default null dimensions to 0.
Chart and Markdown adjustments
src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts, src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs, src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs, src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs, src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts
Chart error message separator changed (period to colon); BitChart.OnAfterRenderAsync unconditionally calls SetupCompletedCallback on first render and discards setup result on subsequent renders; setup/update methods return bool? instead of bool; Markdown viewer and MarkdownViewer.ts support nullable interop outcome and clarify async-false contract.
Tests: FastInvoke behavior, scanner, and contracts
src/BlazorUI/Tests/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScannerTests.cs, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IsRuntimeInvalidFrameworkContractTests.cs, src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs
Adds comprehensive IJSRuntimeFastExtensionsTests covering sync/async behavior across in-process/server/hybrid runtimes, error handling, and timeout paths; introduces TsPromiseMethodScanner to parse TS async methods and validate sync-contract in FastInvokeSyncContractTests; adds framework runtime reflection contract validation; updates project references for ASP.NET Core runtime types; updates AppShell test JSInterop mocks with explicit void result.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 FastInvoke hops and calls with speed,
catching crumbs of JS error with heed.
Nulls are checked, disposals kept neat,
Extras fetch scripts per-URL, so sweet,
Tests watch the contract — synchronous and complete.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs (1)

7-7: ⚠️ Potential issue | 🟡 Minor

Migrate the remaining BitBlazorUI.Utils wrappers to FastInvoke (they’re synchronous)

src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts implements getBodyWidth, setProperty, getProperty, getBoundingClientRect, scrollElementIntoView, selectText, and setStyle as non-async functions that directly return values / void (no Promise usage). Update the corresponding UtilsJsRuntimeExtensions.cs methods to use jsRuntime.FastInvoke / FastInvokeVoid instead of Invoke / InvokeVoid for consistency with BitUtilsToggleOverflow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs`
at line 7, The wrappers in UtilsJsRuntimeExtensions.cs that call
BitBlazorUI.Utils should be converted from synchronous
jsRuntime.Invoke/InvokeVoid to the FastInvoke/FastInvokeVoid helpers used by
BitUtilsToggleOverflow; locate the methods that call
"BitBlazorUI.Utils.getBodyWidth", "setProperty", "getProperty",
"getBoundingClientRect", "scrollElementIntoView", "selectText" and "setStyle"
and replace Invoke<T>/Invoke with jsRuntime.FastInvoke<T>/FastInvoke or
InvokeVoid with FastInvokeVoid while preserving argument lists and return types
so the calls remain synchronous and consistent with the non-async
implementations in Utils.ts.
src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts (1)

15-15: ⚠️ Potential issue | 🟠 Major

Type contract mismatch: setup parameter should accept nullable dotnetObj.

The C# side declares DotNetObjectReference<BitPullToRefresh>? as nullable, but the TS setup method parameter is typed as required DotNetObject. The method will crash at runtime when calling dotnetObj.invokeMethodAsync() (lines 38, 64, 71, 75) if null is passed.

While the current call site (BitPullToRefresh.razor.cs:148) always creates a non-null reference, the type contract allows null and should be enforced defensively.

Recommended fix: Make the parameter optional and add a null guard:

dotnetObj: DotNetObject | undefined) {
    if (!dotnetObj) return;

Alternatively, change the C# signature to use non-nullable DotNetObjectReference<BitPullToRefresh> if null should never be passed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts`
at line 15, The setup function's dotnetObj parameter in BitPullToRefresh.ts is
declared non-nullable but the C# contract allows null; update the setup
signature (in the setup function) to accept DotNetObject | undefined (or
optional) and add an early null guard (if (!dotnetObj) return;) before any calls
to dotnetObj.invokeMethodAsync() (these occur in the setup function and in any
helper closures referenced there such as the touch/move handlers), or
alternatively change the C# DotNetObjectReference<BitPullToRefresh> to be
non-nullable if null will never be passed.
🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts (1)

27-29: ⚡ Quick win

Remove redundant !element.style check.

The !element.style condition is unreachable because HTMLElement always has a style property. Once the !element check passes, element.style is guaranteed to exist.

♻️ Simplify the guard clause
-        public static resetPaneDimensions(element: HTMLElement | undefined) {
-            if (!element || !element.style) return;
-
+        public static resetPaneDimensions(element: HTMLElement | undefined) {
+            if (!element) return;
+
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts` around
lines 27 - 29, The guard in BitSplitter.resetPaneDimensions redundantly checks
!element.style after confirming element exists; remove the unreachable
"!element.style" condition so the guard becomes "if (!element) return;" and
update any surrounding comments accordingly, referencing the public static
method resetPaneDimensions on BitSplitter to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs`:
- Around line 20-23: BitPdfReaderRenderPage and BitPdfReaderRefreshPage are
still calling the old InvokeVoid; change both to use FastInvokeVoid for
consistency with BitPdfReaderDispose (which already uses FastInvokeVoid). Locate
the methods BitPdfReaderRenderPage and BitPdfReaderRefreshPage and replace
jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.renderPage", ...) and
jsRuntime.InvokeVoid("BitBlazorUI.PdfReader.refreshPage", ...) with
jsRuntime.FastInvokeVoid("BitBlazorUI.PdfReader.renderPage", ...) and
jsRuntime.FastInvokeVoid("BitBlazorUI.PdfReader.refreshPage", ...) respectively,
leaving BitPdfReaderSetup (Invoke<int>) unchanged.

---

Outside diff comments:
In
`@src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts`:
- Line 15: The setup function's dotnetObj parameter in BitPullToRefresh.ts is
declared non-nullable but the C# contract allows null; update the setup
signature (in the setup function) to accept DotNetObject | undefined (or
optional) and add an early null guard (if (!dotnetObj) return;) before any calls
to dotnetObj.invokeMethodAsync() (these occur in the setup function and in any
helper closures referenced there such as the touch/move handlers), or
alternatively change the C# DotNetObjectReference<BitPullToRefresh> to be
non-nullable if null will never be passed.

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs`:
- Line 7: The wrappers in UtilsJsRuntimeExtensions.cs that call
BitBlazorUI.Utils should be converted from synchronous
jsRuntime.Invoke/InvokeVoid to the FastInvoke/FastInvokeVoid helpers used by
BitUtilsToggleOverflow; locate the methods that call
"BitBlazorUI.Utils.getBodyWidth", "setProperty", "getProperty",
"getBoundingClientRect", "scrollElementIntoView", "selectText" and "setStyle"
and replace Invoke<T>/Invoke with jsRuntime.FastInvoke<T>/FastInvoke or
InvokeVoid with FastInvokeVoid while preserving argument lists and return types
so the calls remain synchronous and consistent with the non-async
implementations in Utils.ts.

---

Nitpick comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts`:
- Around line 27-29: The guard in BitSplitter.resetPaneDimensions redundantly
checks !element.style after confirming element exists; remove the unreachable
"!element.style" condition so the guard becomes "if (!element) return;" and
update any surrounding comments accordingly, referencing the public static
method resetPaneDimensions on BitSplitter to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 73675b8f-81fa-4452-afd7-53844f2dc3f3

📥 Commits

Reviewing files that changed from the base of the PR and between ea958f8 and f252066.

📒 Files selected for processing (20)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs

@msynk

msynk commented Jun 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

@msynk Sure, I'll re-review the PR now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs`:
- Line 25: The JS interop helper BitUtilsGetBoundingClientRect in
UtilsJsRuntimeExtensions currently returns
jsRuntime.FastInvoke<BoundingClientRect>(...) which can be null; update
BitUtilsGetBoundingClientRect to guard the FastInvoke result and return a
non-null BoundingClientRect (e.g. coalesce to a new BoundingClientRect with
Left/Top/Width/Height = 0) so callers like BitColorPicker.UpdateColor and
BitCircularTimePicker.UpdateTime can safely read properties without null checks
or NREs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 401888d2-353d-481f-b50a-076162576f0f

📥 Commits

Reviewing files that changed from the base of the PR and between f252066 and a37c91e.

📒 Files selected for processing (4)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts

Comment thread src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs Outdated
@msynk

msynk commented Jun 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

@msynk Sure, I'll re-review the PR now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@msynk

msynk commented Jun 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

@msynk Sure, I'll re-review the PR now.

✅ Action performed

Full review finished.

@msynk

msynk commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

@msynk Sure, I'll re-review the PR now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts`:
- Line 42: The current detection of an already-loaded script in the document
(the Array.from(document.scripts).find callback that assigns existingTag) uses
s.src.includes(url), which can produce false positives; change the predicate to
a stricter match — e.g., compare the script src filename or use
s.src.endsWith(url) (or strip query/hash and compare the last path segment) so
only exact filename/ending matches are considered; update the find call that
sets existingTag accordingly to use this precise match logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: bcc2884c-09ab-4c8c-a64e-75e6bb4e0ee6

📥 Commits

Reviewing files that changed from the base of the PR and between e4413ef and f3caf7c.

📒 Files selected for processing (9)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs

Comment thread src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts Outdated
@msynk

msynk commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

@msynk Sure, I'll re-review the PR now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs (1)

15-20: ⚡ Quick win

Serialize the tests that mutate OnError.

IJSRuntimeFastExtensions.OnError is process-global. These cases set/reset it per test, so they can race and flake if MSTest parallelization is enabled for the assembly or CI run. Mark this class [DoNotParallelize] or guard the hook with class-level synchronization.

Also applies to: 76-80, 93-96, 109-112

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs`
around lines 15 - 20, The tests mutate the process-global
IJSRuntimeFastExtensions.OnError which can race when MSTest runs tests in
parallel; update the test class to prevent parallel execution by adding the
MSTest DoNotParallelize attribute (apply to the test class declaration) or
alternatively wrap all accesses to IJSRuntimeFastExtensions.OnError used in the
tests (setup/teardown and tests referencing it at lines with ResetErrorHandler
and the tests at 76-80, 93-96, 109-112) with a class-level lock to serialize
access; pick one approach and apply it consistently so the OnError hook is never
concurrently read/modified across tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts`:
- Around line 44-49: The script-matching currently reduces URLs to pathname only
via the normalize function and then compares normalize(s.src) to targetPath,
which conflates distinct origins and query strings; update the logic so
normalize preserves origin and query (or otherwise compute a full absolute URL
string) when generating targetPath and when comparing against document.scripts,
then use that full URL comparison to find existingTag (refer to the normalize
arrow function, targetPath constant and existingTag lookup) so scripts like
different CDNs or different ?v= queries are considered distinct.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs`:
- Around line 38-40: CollectAsyncJsMethods currently relies on
TsReturnsPromiseRegex to detect Promise-returning methods by body heuristics,
which yields false negatives for delegated returns; instead tighten the
convention so only explicit async functions or those with an explicit Promise
return type are treated as async. Update CollectAsyncJsMethods to stop using
TsReturnsPromiseRegex and only add methods to asyncJsMethods when the method has
the async modifier or its return annotation explicitly contains "Promise";
adjust any test expectations that assumed body-based detection and update
references to TsReturnsPromiseRegex (and related logic) accordingly.

---

Nitpick comments:
In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs`:
- Around line 15-20: The tests mutate the process-global
IJSRuntimeFastExtensions.OnError which can race when MSTest runs tests in
parallel; update the test class to prevent parallel execution by adding the
MSTest DoNotParallelize attribute (apply to the test class declaration) or
alternatively wrap all accesses to IJSRuntimeFastExtensions.OnError used in the
tests (setup/teardown and tests referencing it at lines with ResetErrorHandler
and the tests at 76-80, 93-96, 109-112) with a class-level lock to serialize
access; pick one approach and apply it consistently so the OnError hook is never
concurrently read/modified across tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2defda38-b1ea-4eb2-8611-b1ad9efc9060

📥 Commits

Reviewing files that changed from the base of the PR and between ea958f8 and da5449a.

📒 Files selected for processing (30)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs

Comment thread src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts Outdated
@msynk

msynk commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

@msynk Sure, I'll do a full re-review of the PR now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts`:
- Around line 34-35: Normalize the incoming `url` to the same absolute form used
when matching existing script tags before using it as a cache key: compute the
normalized/absolute URL (the same normalization used when checking
`existingTag.src`) and use that normalized value for all accesses and
assignments to `Extras._scriptPromises` (instead of raw `url`) so lookups,
writes and deletions (the spots around `existingPromise =
Extras._scriptPromises[url]`, the existingTag checks, and the promise
set/resolve/remove locations) consistently use the same key and avoid races
where an equivalent URL misses the cache.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5c6f6c0f-1b50-4b3f-adc9-9d106c7d049a

📥 Commits

Reviewing files that changed from the base of the PR and between ea958f8 and 4213ff7.

📒 Files selected for processing (30)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs

Comment thread src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts Outdated
@msynk

msynk commented Jun 13, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

@msynk Sure, I'll do a full re-review of the PR now.

✅ Action performed

Full review finished.

@msynk

msynk commented Jun 13, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

@msynk Sure, I'll do a full re-review of the PR now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs (1)

25-34: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against multiple Init() calls to prevent reference leak.

If Init() is called multiple times, a new DotNetObjectReference is created at line 31 without disposing the previous _dotnetObj, causing a resource leak. The _isInitialized check at line 27 prevents re-initialization logic, but the reference would already be leaked before the check returns.

🔒 Proposed fix to dispose existing reference before creating new one
     public async Task Init()
     {
         if (_isInitialized) return;

         _isInitialized = true;

+        _dotnetObj?.Dispose();
         _dotnetObj = DotNetObjectReference.Create(this);

         await js.InvokeVoid("BitBlazorUI.PageVisibility.init", _dotnetObj);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs` around lines 25 - 34,
Init() in BitPageVisibility creates a new DotNetObjectReference (_dotnetObj)
without disposing a prior instance, causing a leak; before assigning a new
DotNetObjectReference in Init(), check if _dotnetObj is non-null and call
Dispose() (and set it null) to release the previous reference, then proceed to
create the new DotNetObjectReference and call
js.InvokeVoid("BitBlazorUI.PageVisibility.init", _dotnetObj); keep the existing
_isInitialized guard logic intact.
🧹 Nitpick comments (2)
src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs (2)

259-302: 💤 Low value

Document the backward scanning limitations for maintainability.

IsNestedFunctionBodyOpen performs backward scanning to detect function/closure bodies, which is inherently fragile (as noted in the SkipNonCodeBackward doc comment). The current implementation cannot reliably distinguish between regex literals (/pattern/) and division (a / b), and it may misclassify other constructs.

While the doc comment at line 411-415 explains this risk, consider adding a specific comment near line 259 explaining:

  1. Why backward scanning is acceptable here (only affects secondary body scan, not primary async/Promise detection)
  2. What specific misclassifications could occur (regex literals, template literals with embedded code)
  3. That the tradeoff favors false negatives (missing some nested functions) over false positives

This will help future maintainers understand the design choice and avoid "fixing" the scanner in a way that introduces false positives.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs`
around lines 259 - 302, Add a focused inline comment above the
IsNestedFunctionBodyOpen method explaining why backward scanning (via
SkipNonCodeBackward and FindMatchingBackward) is acceptable here (it only
affects secondary nested-body detection, not primary async/Promise detection),
enumerate likely misclassifications (e.g., confusing regex literals like
/pattern/ with division, template literals with embedded expressions, and other
edge-case token boundaries), and state the deliberate tradeoff to prefer false
negatives (missing some nested functions) over false positives so maintainers
don’t attempt aggressive fixes that would introduce incorrect matches; reference
the existing SkipNonCodeBackward doc comment in the note.

153-200: ⚡ Quick win

Review the brace stack logic for nested function tracking.

The ScanTopLevelReturns method uses a Stack<bool> to track whether each { belongs to a function body, incrementing nestedFunctionDepth when pushing true. However, the pop at line 176 doesn't verify that the closing } actually matches the opening { type – it only checks braceStack.Count > 0. If a non-function { is followed by a function { and then two }, the depth tracking could desync.

For example:

static method() {
  if (cond) {           // push false
    const fn = () => {  // push true, depth++
      // ...
    };                  // pop true, depth--, stack now [false]
  }                     // pop false, depth unchanged, stack now []
  return fetch(url);    // depth == 0, detected as top-level ✓
}

This appears correct in normal cases. The risk is if the scanner misses a { (e.g., inside a comment or string literal not fully skipped), leading to an unbalanced stack. The SkipNonCode logic should prevent this, but it's a subtle interaction.

Consider adding an assertion or test case that validates balanced braces across various nesting scenarios (function in if-block, multiple closures, etc.) to ensure the stack never goes negative or becomes desynchronized.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs`
around lines 153 - 200, ScanTopLevelReturns can desynchronize
nestedFunctionDepth when braceStack is unbalanced; ensure popping only occurs
when there is a matching pushed item and validate what was popped before
decrementing depth. Change the brace-close handling in ScanTopLevelReturns to
check braceStack.Count > 0 before Pop (already present) but also capture the
popped value (e.g., var poppedIsFunction = braceStack.Pop()) and only decrement
nestedFunctionDepth when poppedIsFunction is true; additionally add assertions
or unit tests exercising ScanTopLevelReturns with nested non-function and
function `{` sequences (including cases with comments/strings that could confuse
SkipNonCode) to verify the stack never goes negative and depth stays consistent;
reference methods: ScanTopLevelReturns, braceStack, nestedFunctionDepth,
SkipNonCode, IsNestedFunctionBodyOpen, SkipNonMatchingReturnExpression.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts`:
- Around line 20-48: BitSwiper.getDimensions should return null on failure
instead of an empty object: change the early-return when !root || !container to
`return null;` and in the catch block return `null` as well; ensure callers
(e.g., BitSwiper.razor.cs which already uses `dimensions?.X ?? 0`) continue to
work with nullable results and, if necessary, update any TypeScript signature or
documentation for getDimensions to indicate a nullable SwiperDimensions return.

In
`@src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs`:
- Line 12: The method BitBlazorUI.Swiper.getDimensions call in
BitSwiperJsRuntimeExtensions.cs currently returns a non-nullable
SwiperDimensions but calls jsRuntime.FastInvoke<SwiperDimensions> which can
return null; change the method signature to return ValueTask<SwiperDimensions?>
and propagate nullable handling (i.e., return the FastInvoke result as-is) so
callers can handle nulls, matching the nullable pattern used in
BitSplitterGetSplitterWidth and BitUtilsGetBodyWidth.

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs`:
- Around line 19-21: The BitUtilsGetProperty method currently returns
ValueTask<string> but calls jsRuntime.FastInvoke<string> which can yield null;
change the method signature to return ValueTask<string?> and adjust any nullable
annotations accordingly so the method expresses the nullable contract; update
callers (or their null-handling) if they assume non-null, and keep the method
body using jsRuntime.FastInvoke<string> unchanged (only the return type and any
immediate null-related annotations/flow must be updated).

---

Outside diff comments:
In `@src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs`:
- Around line 25-34: Init() in BitPageVisibility creates a new
DotNetObjectReference (_dotnetObj) without disposing a prior instance, causing a
leak; before assigning a new DotNetObjectReference in Init(), check if
_dotnetObj is non-null and call Dispose() (and set it null) to release the
previous reference, then proceed to create the new DotNetObjectReference and
call js.InvokeVoid("BitBlazorUI.PageVisibility.init", _dotnetObj); keep the
existing _isInitialized guard logic intact.

---

Nitpick comments:
In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs`:
- Around line 259-302: Add a focused inline comment above the
IsNestedFunctionBodyOpen method explaining why backward scanning (via
SkipNonCodeBackward and FindMatchingBackward) is acceptable here (it only
affects secondary nested-body detection, not primary async/Promise detection),
enumerate likely misclassifications (e.g., confusing regex literals like
/pattern/ with division, template literals with embedded expressions, and other
edge-case token boundaries), and state the deliberate tradeoff to prefer false
negatives (missing some nested functions) over false positives so maintainers
don’t attempt aggressive fixes that would introduce incorrect matches; reference
the existing SkipNonCodeBackward doc comment in the note.
- Around line 153-200: ScanTopLevelReturns can desynchronize nestedFunctionDepth
when braceStack is unbalanced; ensure popping only occurs when there is a
matching pushed item and validate what was popped before decrementing depth.
Change the brace-close handling in ScanTopLevelReturns to check braceStack.Count
> 0 before Pop (already present) but also capture the popped value (e.g., var
poppedIsFunction = braceStack.Pop()) and only decrement nestedFunctionDepth when
poppedIsFunction is true; additionally add assertions or unit tests exercising
ScanTopLevelReturns with nested non-function and function `{` sequences
(including cases with comments/strings that could confuse SkipNonCode) to verify
the stack never goes negative and depth stays consistent; reference methods:
ScanTopLevelReturns, braceStack, nestedFunctionDepth, SkipNonCode,
IsNestedFunctionBodyOpen, SkipNonMatchingReturnExpression.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1d022c3a-2d6a-4923-8cf2-0013dbfd4680

📥 Commits

Reviewing files that changed from the base of the PR and between f7ba0b3 and ffe7f4c.

📒 Files selected for processing (52)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Services/BitMarkdownService.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/Overlay/BitOverlay.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.ts
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Swipes.ts
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IsRuntimeInvalidFrameworkContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScannerTests.cs

Comment thread src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts
Comment thread src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs Outdated
Comment thread src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs Outdated
@msynk

msynk commented Jun 13, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

@msynk Sure, I'll do a full re-review of the PR now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs (1)

20-43: ⚠️ Potential issue | 🟡 Minor

Migrate remaining synchronous JS interop to FastInvokeVoid

In Extras.ts, initScripts and initStylesheets are async (Promise-returning), so keeping BitExtrasInitScripts / BitExtrasInitStylesheets on InvokeVoid is fine. However, setPreventKeys, disposePreventKeys, and scrollOptionIntoView are synchronous (not async), so BitExtrasSetPreventKeys, BitExtrasDisposePreventKeys, and BitExtrasScrollOptionIntoView should be switched to FastInvokeVoid to match the migration intent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs`
around lines 20 - 43, Change the three synchronous JS interop calls to use
FastInvokeVoid: in BitExtrasSetPreventKeys, BitExtrasDisposePreventKeys, and
BitExtrasScrollOptionIntoView replace jsRuntime.InvokeVoid(...) with
jsRuntime.FastInvokeVoid(...) while keeping the same arguments (element, keys)
and (element) and (optionId) respectively so the signatures and behavior remain
unchanged.
🧹 Nitpick comments (1)
src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs (1)

157-157: ⚡ Quick win

Result is discarded on non-first render but used on first render.

Line 145 uses the JS interop result to determine chart readiness, but line 157 discards it with _. This inconsistency suggests either:

  • The result matters only on first render (current behavior), or
  • The result should be checked on subsequent renders as well.

Verify that ignoring the setup result on non-first renders is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs` at line
157, The code currently discards the JS interop result when calling
BitChartJsSetupChart(Config) on non-first renders; either preserve the original
readiness check or make the intent explicit. Update the call in
BitChart.razor.cs to capture the result (e.g., var result = await
_js.BitChartJsSetupChart(Config)) and apply the same readiness logic used on
first render (set the same readiness flag or call the same method that handles
the result), or if ignoring the result is intentional, add a clear comment
explaining why and keep the discard. Ensure you reference and update the same
readiness state/flag used elsewhere in the class so behavior is consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs`:
- Around line 145-150: The code currently treats Config is null as "ready" and
invokes SetupCompletedCallback without calling the interop; change the flow so
the chart setup interop is always awaited and determines readiness: call await
_js.BitChartJsSetupChart(Config) (remove/avoid short-circuiting on Config is
null) and only set chartReady/raise SetupCompletedCallback when that interop
returns true; update the logic around the chartReady local, the call to
_js.BitChartJsSetupChart(Config), and the conditional that invokes
SetupCompletedCallback so the callback only fires after the JS setup completes.

In
`@src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs`:
- Around line 15-20: Update the explanatory comment in
BitDataGridJsRuntimeExtensions (the block describing why the code uses the
regular async IJS invocation) to remove the incorrect claim that
IJSRuntimeFastExtensions.FastInvokeVoid "only swallows JsonException" and
instead state the correct reason: FastInvokeVoid (per
IJSRuntimeFastExtensions.FastInvokeVoid) runs synchronously on WebAssembly and
can alter Promise/ordering and error propagation semantics, so we deliberately
use the async Invoke pattern to ensure that JS-side failures are contained
within the returned Task and do not escape synchronously into the render loop;
keep the rest of the explanation about DOM-heavy positioning
(getBoundingClientRect, scrollIntoViewIfNeeded, focus) intact.

In `@src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts`:
- Around line 138-172: The non-complete branch of awaitHostResource delays even
when the host resource is already applied; change awaitHostResource to perform
the same immediate checks used in the document.readyState === 'complete' branch
before attaching listeners: for kind === 'stylesheet' call
Extras.isHostStylesheetApplied(element as HTMLLinkElement) and for kind ===
'script' call Extras.isHostScriptLoaded(element as HTMLScriptElement), and if
the check shows the resource is present/loaded return Promise.resolve(),
otherwise proceed to create the Promise and add the load/error/window listeners;
keep using Extras.loadResourceError(...) for rejection when checks fail.

In `@src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts`:
- Around line 11-19: The getSplitterWidth function should return null on
geometry-read failures instead of 0 to preserve the JS↔C# nullable contract:
change the signature to return number | null, return null in the early guard
(when !element or missing getBoundingClientRect) and return null in the catch
block rather than 0; apply the same changes to the sibling function
getSplitterHeight (update its signature to number | null and return null on
invalid element or exceptions).

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs`:
- Around line 19-22: The BitUtilsGetProperty extension returns
ValueTask<string?> but calls jsRuntime.FastInvoke<string>; change the generic
parameter to FastInvoke<string?> to match the nullable contract (i.e., update
the call inside BitUtilsGetProperty to
jsRuntime.FastInvoke<string?>("BitBlazorUI.Utils.getProperty", element,
property)) so the method signature and invocation nullability are consistent;
ensure using IJSRuntime's FastInvoke overload that accepts the nullable generic.

---

Outside diff comments:
In
`@src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs`:
- Around line 20-43: Change the three synchronous JS interop calls to use
FastInvokeVoid: in BitExtrasSetPreventKeys, BitExtrasDisposePreventKeys, and
BitExtrasScrollOptionIntoView replace jsRuntime.InvokeVoid(...) with
jsRuntime.FastInvokeVoid(...) while keeping the same arguments (element, keys)
and (element) and (optionId) respectively so the signatures and behavior remain
unchanged.

---

Nitpick comments:
In `@src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs`:
- Line 157: The code currently discards the JS interop result when calling
BitChartJsSetupChart(Config) on non-first renders; either preserve the original
readiness check or make the intent explicit. Update the call in
BitChart.razor.cs to capture the result (e.g., var result = await
_js.BitChartJsSetupChart(Config)) and apply the same readiness logic used on
first render (set the same readiness flag or call the same method that handles
the result), or if ignoring the result is intentional, add a clear comment
explaining why and keep the discard. Ensure you reference and update the same
readiness state/flag used elsewhere in the class so behavior is consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5a130237-07d6-468a-afa9-3bb22c8ec590

📥 Commits

Reviewing files that changed from the base of the PR and between f7ba0b3 and 10da4a1.

📒 Files selected for processing (53)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Services/BitMarkdownService.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Slider/BitSlider.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/Overlay/BitOverlay.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.ts
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Swipes.ts
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IsRuntimeInvalidFrameworkContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScannerTests.cs

Comment thread src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs Outdated
Comment thread src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
Comment thread src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts Outdated
@msynk

msynk commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

@msynk Sure, I'll do a full re-review of the PR now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs`:
- Around line 140-147: The InvokeAsync call on line 140 that schedules
ResetPaneDimensionsOnVerticalChange is being discarded as a fire-and-forget
operation, which means exceptions from the BitSplitterResetPaneDimensions JS
interop calls will be unobserved and silently fail. Add proper error handling to
the InvokeAsync task by attaching a continuation (such as using .ContinueWith or
.Catch) to handle any exceptions that occur during the pane dimension reset,
ensuring failures are logged or handled appropriately rather than being silently
ignored.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 66f2a46d-90ae-489c-b234-252b4efabf0e

📥 Commits

Reviewing files that changed from the base of the PR and between f7ba0b3 and 5a2c1ab.

📒 Files selected for processing (54)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Services/BitMarkdownService.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Slider/BitSlider.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/Overlay/BitOverlay.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.ts
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/Swipes.ts
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IsRuntimeInvalidFrameworkContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScannerTests.cs

Comment thread src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs Outdated
@msynk

msynk commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai re-review

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

@msynk Sure, I'll do a full re-review of the PR now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs (1)

20-28: ⚠️ Potential issue | 🔴 Critical

Change to InvokeVoidAsync instead of InvokeVoid.

Both BitExtrasInitScripts and BitExtrasInitStylesheets call async JavaScript functions that return Promises and must be awaited. The JavaScript implementations use await Promise.allSettled() to ensure all resources are loaded before resolving. Using InvokeVoid() is fire-and-forget and returns a completed ValueTask immediately, breaking the async contract that callers expect. All call sites use await, expecting the methods to wait for resource loading to complete.

Use InvokeVoidAsync() instead, which properly awaits the JavaScript Promise and returns a ValueTask that completes only after the JS function resolves.

Suggested fix
public static ValueTask BitExtrasInitScripts(this IJSRuntime jsRuntime, IEnumerable<string> scripts, bool isModule = false)
{
    return jsRuntime.InvokeVoidAsync("BitBlazorUI.Extras.initScripts", scripts, isModule);
}

public static ValueTask BitExtrasInitStylesheets(this IJSRuntime jsRuntime, IEnumerable<string> stylesheets)
{
    return jsRuntime.InvokeVoidAsync("BitBlazorUI.Extras.initStylesheets", stylesheets);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs`
around lines 20 - 28, The methods BitExtrasInitScripts and
BitExtrasInitStylesheets are using InvokeVoid() which is a fire-and-forget
operation that returns an immediately completed ValueTask, but the underlying
JavaScript functions return Promises that must be awaited before resources are
fully loaded. Replace InvokeVoid() with InvokeVoidAsync() in both method
implementations to properly await the JavaScript Promise resolution and ensure
callers' await statements actually wait for resource loading to complete.
🧹 Nitpick comments (4)
src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs (1)

130-135: 💤 Low value

Minor: redundant copy to result set.

Lines 130-133 copy promiseMethods into result, but result is empty at this point and could be eliminated by directly returning promiseMethods. This is harmless but adds a small allocation and iteration.

♻️ Simplification
-        foreach (var key in promiseMethods)
-        {
-            result.Add(key);
-        }
-
-        return result;
+        return promiseMethods;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs`
around lines 130 - 135, The foreach loop in lines 130-133 unnecessarily copies
each item from `promiseMethods` into the empty `result` collection. Since
`result` starts empty and is only populated with items from `promiseMethods`,
eliminate this redundant loop by directly returning `promiseMethods` instead of
copying its contents to `result`. This removes an unnecessary allocation and
iteration while maintaining the same behavior.
src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs (2)

62-64: 💤 Low value

Class-name uniqueness assumption in normalization.

LastTwoSegments reduces identifiers to Class.method form, which assumes class names are unique across the scanned TypeScript sources. If multiple classes share the same name (e.g., Namespace1.Utils and Namespace2.Utils), a FastInvoke call to the synchronous method in one namespace could be flagged if the same method name in the other namespace is async. Given the single-project scope and typical TypeScript structure, this is a reasonable tradeoff, but worth noting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs`
around lines 62 - 64, The LastTwoSegments method reduces identifiers to
Class.method form by assuming class names are unique across TypeScript sources,
which could cause false positives if multiple classes share the same name across
different namespaces. Add a comment near the LastTwoSegments call explaining
this assumption and documenting why it is acceptable for the current
single-project scope and typical TypeScript structure, making the tradeoff
explicit for future maintainers.

75-79: ⚖️ Poor tradeoff

Evolution from past review: delegation gap now explicit design choice.

The past review flagged that Promise detection could miss delegated returns. The current implementation has evolved:

  1. Delegation IS propagated from methods with explicit async or : Promise<...> annotations (see TsPromiseMethodScanner lines 101-117)
  2. Delegation is NOT propagated from body-scanned methods (those detected via return fetch(...) heuristics)

This tradeoff is now explicitly documented in TsPromiseMethodScanner (lines 19-22) and tested (TsPromiseMethodScannerTests.cs lines 135-152). The conservative approach prevents heuristic false positives from cascading through delegation chains. If the codebase convention ensures all Promise-returning methods are explicitly marked async or have : Promise<...> return types, this gap is acceptable. Otherwise, consider either removing body scanning entirely (require explicit annotations) or accepting the documented limitation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs`
around lines 75 - 79, Ensure that the design tradeoff regarding Promise
delegation propagation is explicitly and clearly documented in
TsPromiseMethodScanner (around lines 19-22), specifically stating that
delegation is propagated from methods with explicit async or : Promise<...>
annotations but NOT from body-scanned heuristics like return fetch(...). Verify
that TsPromiseMethodScannerTests (around lines 135-152) includes tests that
validate this behavior and demonstrate the documented limitations. Additionally,
confirm that the implementation in TsPromiseMethodScanner (lines 101-117)
correctly implements this explicit design choice by only propagating delegation
from explicitly annotated methods while intentionally excluding
heuristically-detected methods.
src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts (1)

26-30: ⚡ Quick win

Add context prefix to error logging for consistency.

The error log on line 29 lacks the "BitBlazorUI.ColorPicker.dispose:" prefix used consistently in BitSwiper.ts, BitSplitter.ts, and other files in this layer, making debugging slightly harder.

♻️ Suggested fix
             try {
                 bitController?.dotnetObj?.dispose();
             } catch (e) {
-                console.error(e);
+                console.error("BitBlazorUI.ColorPicker.dispose:", e);
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts`
around lines 26 - 30, The console.error call in the catch block of the dispose
method lacks the context prefix used consistently across other components in
this layer. Add the prefix "BitBlazorUI.ColorPicker.dispose:" to the error log
in the catch block (where the console.error(e) statement is located) to match
the logging convention used in BitSwiper.ts, BitSplitter.ts, and other files,
improving debugging clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs`:
- Around line 150-155: The XML documentation for SetupCompletedCallback states
it fires when the chart has been setup through interop and the JavaScript object
is available, but the current implementation invokes it unconditionally on first
render regardless of whether Config is null or setup succeeded. Either move the
await SetupCompletedCallback.InvokeAsync(this) call to execute only when setup
completes successfully by placing it inside the if (Config is not null) block,
or update the XML documentation comments to accurately reflect that the callback
fires unconditionally on first render. The recommended approach is to move the
callback invocation to only fire when setup actually succeeds so that consumers
relying on this callback to signal chart readiness do not receive false
positives.

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs`:
- Around line 98-104: Remove the null check from the IsRuntimeInvalid method
that returns true when jsRuntime is null. The method should not treat null as an
invalid runtime state; instead null should propagate naturally as a caller error
to fail fast. Reserve IsRuntimeInvalid() only for detecting real framework
runtime states like prerendering or disconnection, not for catching null
reference bugs which should throw immediately rather than silently no-op in
downstream Invoke*/FastInvoke* calls.

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs`:
- Around line 175-181: The ReportIfUnexpectedNull method is causing double error
reporting because it cannot distinguish between a null result that came from an
already-handled exception in FastInvoke* methods versus a truly unexpected null
from a valid runtime. The fix requires either: (1) modifying the contract so
that FastInvoke* methods return a result wrapper or sentinel value that
indicates whether the null came from an already-reported error, allowing
ReportIfUnexpectedNull to skip reporting in that case, or (2) moving the null
result validation logic entirely into the FastInvoke* methods themselves so that
ReportIfUnexpectedNull is not needed as a defensive check. Choose the approach
that best fits your architecture and ensure that only the initial interop
failure is reported, not a secondary InvalidOperationException for the same
underlying issue.

In `@src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs`:
- Around line 50-59: The Dispose method in BitPageVisibility is performing a
fire-and-forget JS interop call to "BitBlazorUI.PageVisibility.dispose" which
hides teardown failures and leaves the JS-side listener uncleared. Implement
IAsyncDisposable on the BitPageVisibility class instead of IDisposable, and
replace the Dispose method with a DisposeAsync method. In DisposeAsync, properly
await the JS cleanup operation using js.InvokeAsync instead of the
fire-and-forget js.InvokeVoid call, ensuring the _isInitialized flag is reset
after the async operation completes. This allows Blazor's DI lifecycle to
properly handle the asynchronous disposal of this scoped service.

---

Outside diff comments:
In
`@src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs`:
- Around line 20-28: The methods BitExtrasInitScripts and
BitExtrasInitStylesheets are using InvokeVoid() which is a fire-and-forget
operation that returns an immediately completed ValueTask, but the underlying
JavaScript functions return Promises that must be awaited before resources are
fully loaded. Replace InvokeVoid() with InvokeVoidAsync() in both method
implementations to properly await the JavaScript Promise resolution and ensure
callers' await statements actually wait for resource loading to complete.

---

Nitpick comments:
In `@src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts`:
- Around line 26-30: The console.error call in the catch block of the dispose
method lacks the context prefix used consistently across other components in
this layer. Add the prefix "BitBlazorUI.ColorPicker.dispose:" to the error log
in the catch block (where the console.error(e) statement is located) to match
the logging convention used in BitSwiper.ts, BitSplitter.ts, and other files,
improving debugging clarity.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs`:
- Around line 62-64: The LastTwoSegments method reduces identifiers to
Class.method form by assuming class names are unique across TypeScript sources,
which could cause false positives if multiple classes share the same name across
different namespaces. Add a comment near the LastTwoSegments call explaining
this assumption and documenting why it is acceptable for the current
single-project scope and typical TypeScript structure, making the tradeoff
explicit for future maintainers.
- Around line 75-79: Ensure that the design tradeoff regarding Promise
delegation propagation is explicitly and clearly documented in
TsPromiseMethodScanner (around lines 19-22), specifically stating that
delegation is propagated from methods with explicit async or : Promise<...>
annotations but NOT from body-scanned heuristics like return fetch(...). Verify
that TsPromiseMethodScannerTests (around lines 135-152) includes tests that
validate this behavior and demonstrate the documented limitations. Additionally,
confirm that the implementation in TsPromiseMethodScanner (lines 101-117)
correctly implements this explicit design choice by only propagating delegation
from explicitly annotated methods while intentionally excluding
heuristically-detected methods.

In
`@src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs`:
- Around line 130-135: The foreach loop in lines 130-133 unnecessarily copies
each item from `promiseMethods` into the empty `result` collection. Since
`result` starts empty and is only populated with items from `promiseMethods`,
eliminate this redundant loop by directly returning `promiseMethods` instead of
copying its contents to `result`. This removes an unnecessary allocation and
iteration while maintaining the same behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dfc615b4-856f-49f8-b792-bb6df3b8235a

📥 Commits

Reviewing files that changed from the base of the PR and between f7ba0b3 and d412b02.

📒 Files selected for processing (59)
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShellJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/JsInterop/BitChartJsInterop.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGrid.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/DataGrid/BitDataGridJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/InfiniteScrolling/BitInfiniteScrollingJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewer.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/MarkdownViewer/BitMarkdownViewerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PdfReader/BitPdfReaderJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Components/ProModal/BitProModal.razor.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Extensions/JsInterop/ExtrasJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI.Extras/Scripts/Extras.ts
  • src/BlazorUI/Bit.BlazorUI.Extras/Services/BitMarkdownService.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPicker.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/ColorPicker/BitColorPickerJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUploadJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInputJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBoxJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/Slider/BitSlider.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiper.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Lists/Swiper/BitSwiperJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Dialog/BitDialog.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Panel/BitPanel.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitter.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Splitter/BitSplitterJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/Overlay/BitOverlay.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefresh.ts
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/PullToRefresh/BitPullToRefreshJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.razor.cs
  • src/BlazorUI/Bit.BlazorUI/Components/Utilities/SwipeTrap/BitSwipeTrap.ts
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/JsInteropConstants.cs
  • src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/UtilsJsRuntimeExtensions.cs
  • src/BlazorUI/Bit.BlazorUI/Scripts/PageVisibility.ts
  • src/BlazorUI/Bit.BlazorUI/Scripts/Swipes.ts
  • src/BlazorUI/Bit.BlazorUI/Scripts/Utils.ts
  • src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Components/Extras/AppShell/BitAppShellTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/FastInvokeSyncContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IJSRuntimeFastExtensionsTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/IsRuntimeInvalidFrameworkContractTests.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScanner.cs
  • src/BlazorUI/Tests/Bit.BlazorUI.Tests/Extensions/JsInterop/TsPromiseMethodScannerTests.cs

Comment on lines +150 to +155
// Always signal completion on first render, matching the long-standing behavior: consumers may
// rely on SetupCompletedCallback firing once the component has rendered regardless of whether the
// chart setup itself succeeded (e.g. when Config is still null, or when interop was unavailable
// and the result was swallowed on the WebAssembly fast path).
await SetupCompletedCallback.InvokeAsync(this);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

SetupCompletedCallback documentation contradicts its actual firing condition.

The XML documentation (lines 30–33) states the callback fires "when the chart has been setup through interop and the JavaScript chart object is available," but the code now invokes it unconditionally on first render regardless of whether Config is null or setup succeeded. Consumers relying on this callback to signal "chart is ready" will receive false positives when Config is null or when the interop call fails/returns false on WebAssembly's fast path.

Either update the XML docs to match the actual behavior (fires after first render regardless of setup outcome), or move the callback invocation inside the if (Config is not null) block and only invoke it when setup completes successfully.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI.Extras/Components/Chart/BitChart.razor.cs` around
lines 150 - 155, The XML documentation for SetupCompletedCallback states it
fires when the chart has been setup through interop and the JavaScript object is
available, but the current implementation invokes it unconditionally on first
render regardless of whether Config is null or setup succeeded. Either move the
await SetupCompletedCallback.InvokeAsync(this) call to execute only when setup
completes successfully by placing it inside the if (Config is not null) block,
or update the XML documentation comments to accurately reflect that the callback
fires unconditionally on first render. The recommended approach is to move the
callback invocation to only fire when setup actually succeeds so that consumers
relying on this callback to signal chart readiness do not receive false
positives.

Comment on lines +98 to +104
[SuppressMessage("Trimming", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The return value of the source method does not have matching annotations.", Justification = "Reflection here only reads a well-known bool property (RemoteJSRuntime.IsInitialized) and a private field (WebViewJSRuntime._ipcSender) by name for host-runtime detection; no members are dynamically invoked or instantiated, and a missing member is handled by treating the runtime as valid, so trimming cannot break this probe.")]
public static bool IsRuntimeInvalid(this IJSRuntime jsRuntime)
{
if (jsRuntime is null) return false;
// A null runtime can't service interop, so report it as invalid. This lets the no-op paths in
// InvokeVoid/Invoke/FastInvoke skip the call instead of dereferencing null (the async fallback
// would otherwise throw ArgumentNullException from the framework's JSRuntimeExtensions).
if (jsRuntime is null) return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t treat a null runtime as an “invalid host runtime”.

null here is a caller bug, not a prerender/disconnected framework runtime. Returning true makes every Invoke*/FastInvoke* wrapper silently no-op or hand back defaults, which will hide missing DI/initialization defects and leave components half-initialized instead of failing fast. Keep throwing on null and reserve IsRuntimeInvalid() for real framework runtime states only.

Suggested fix
-        if (jsRuntime is null) return true;
+        ArgumentNullException.ThrowIfNull(jsRuntime);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[SuppressMessage("Trimming", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The return value of the source method does not have matching annotations.", Justification = "Reflection here only reads a well-known bool property (RemoteJSRuntime.IsInitialized) and a private field (WebViewJSRuntime._ipcSender) by name for host-runtime detection; no members are dynamically invoked or instantiated, and a missing member is handled by treating the runtime as valid, so trimming cannot break this probe.")]
public static bool IsRuntimeInvalid(this IJSRuntime jsRuntime)
{
if (jsRuntime is null) return false;
// A null runtime can't service interop, so report it as invalid. This lets the no-op paths in
// InvokeVoid/Invoke/FastInvoke skip the call instead of dereferencing null (the async fallback
// would otherwise throw ArgumentNullException from the framework's JSRuntimeExtensions).
if (jsRuntime is null) return true;
[SuppressMessage("Trimming", "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The return value of the source method does not have matching annotations.", Justification = "Reflection here only reads a well-known bool property (RemoteJSRuntime.IsInitialized) and a private field (WebViewJSRuntime._ipcSender) by name for host-runtime detection; no members are dynamically invoked or instantiated, and a missing member is handled by treating the runtime as valid, so trimming cannot break this probe.")]
public static bool IsRuntimeInvalid(this IJSRuntime jsRuntime)
{
// A null runtime can't service interop, so report it as invalid. This lets the no-op paths in
// InvokeVoid/Invoke/FastInvoke skip the call instead of dereferencing null (the async fallback
// would otherwise throw ArgumentNullException from the framework's JSRuntimeExtensions).
ArgumentNullException.ThrowIfNull(jsRuntime);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs`
around lines 98 - 104, Remove the null check from the IsRuntimeInvalid method
that returns true when jsRuntime is null. The method should not treat null as an
invalid runtime state; instead null should propagate naturally as a caller error
to fail fast. Reserve IsRuntimeInvalid() only for detecting real framework
runtime states like prerendering or disconnection, not for catching null
reference bugs which should throw immediately rather than silently no-op in
downstream Invoke*/FastInvoke* calls.

Comment on lines +175 to +181
public static T? ReportIfUnexpectedNull<T>(this IJSRuntime jsRuntime, string identifier, T? result)
where T : class
{
if (result is not null || jsRuntime.IsRuntimeInvalid()) return result;

ReportError(identifier, new InvalidOperationException("The interop call completed without a result."));
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

ReportIfUnexpectedNull now double-reports real FastInvoke failures.

When FastInvoke* catches JsonException/JSException, it already calls ReportError and returns null. Any caller that then passes that result through this helper turns one interop failure into two reports: the original exception and a second InvalidOperationException. That will inflate alerts/logs and it contradicts the remarks above that only the “valid runtime, null without exception” case should be reported here. This needs a contract that preserves whether the null came from a swallowed error, or the unexpected-null detection needs to move into FastInvoke* itself.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeFastExtensions.cs`
around lines 175 - 181, The ReportIfUnexpectedNull method is causing double
error reporting because it cannot distinguish between a null result that came
from an already-handled exception in FastInvoke* methods versus a truly
unexpected null from a valid runtime. The fix requires either: (1) modifying the
contract so that FastInvoke* methods return a result wrapper or sentinel value
that indicates whether the null came from an already-reported error, allowing
ReportIfUnexpectedNull to skip reporting in that case, or (2) moving the null
result validation logic entirely into the FastInvoke* methods themselves so that
ReportIfUnexpectedNull is not needed as a defensive check. Choose the approach
that best fits your architecture and ensure that only the initial interop
failure is reported, not a secondary InvalidOperationException for the same
underlying issue.

Comment on lines +50 to +59
public void Dispose()
{
if (_isInitialized)
{
_isInitialized = false;

// Tears down the global visibilitychange listener and resets the JS-side init guard,
// so the now-disposed _dotnetObj is no longer referenced and a future instance can re-init.
_ = js.InvokeVoid("BitBlazorUI.PageVisibility.dispose");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "BitPageVisibility.cs" | head -20

Repository: bitfoundation/bitplatform

Length of output: 124


🏁 Script executed:

fd -t f "BitPageVisibility\.cs"

Repository: bitfoundation/bitplatform

Length of output: 122


🏁 Script executed:

rg "class BitPageVisibility" -A 30 -B 5

Repository: bitfoundation/bitplatform

Length of output: 2929


🏁 Script executed:

rg "BitPageVisibility" --type cs -l

Repository: bitfoundation/bitplatform

Length of output: 359


🏁 Script executed:

cat -n src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs

Repository: bitfoundation/bitplatform

Length of output: 2109


🏁 Script executed:

rg "BitPageVisibility" -B 5 -A 10 src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs

Repository: bitfoundation/bitplatform

Length of output: 483


🏁 Script executed:

rg "BitPageVisibility" -B 3 -A 10 src/BlazorUI/Bit.BlazorUI/Extensions/IBitBlazorUIServiceCollectionExtensions.cs

Repository: bitfoundation/bitplatform

Length of output: 305


🏁 Script executed:

rg "\.Dispose\(\)" src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs -B 3 -A 3

Repository: bitfoundation/bitplatform

Length of output: 736


🏁 Script executed:

rg "_pageVisibility" src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs -B 2 -A 2

Repository: bitfoundation/bitplatform

Length of output: 613


🏁 Script executed:

cat -n src/BlazorUI/Bit.BlazorUI/Components/Lists/Carousel/BitCarousel.razor.cs | grep -A 20 "public async Task"

Repository: bitfoundation/bitplatform

Length of output: 1550


Implement IAsyncDisposable to properly await JS teardown instead of fire-and-forget in Dispose.

On line 58, _ = js.InvokeVoid("BitBlazorUI.PageVisibility.dispose") is a fire-and-forget call that discards the ValueTask. This hides teardown failures and can leave the JS-side listener uncleared. Since Init() already uses async JS interop, move the JS cleanup to DisposeAsync() and let Blazor's DI lifecycle handle async disposal for this scoped service.

Proposed fix
-public class BitPageVisibility(IJSRuntime js) : IDisposable
+public class BitPageVisibility(IJSRuntime js) : IDisposable, IAsyncDisposable
 {
     private bool _isInitialized;
     private DotNetObjectReference<BitPageVisibility>? _dotnetObj;
@@
     public void Dispose()
     {
+        _dotnetObj?.Dispose();
+    }
+
+    public async ValueTask DisposeAsync()
+    {
         if (_isInitialized)
         {
             _isInitialized = false;
-            _ = js.InvokeVoid("BitBlazorUI.PageVisibility.dispose");
+            try
+            {
+                await js.InvokeVoid("BitBlazorUI.PageVisibility.dispose");
+            }
+            catch (JSDisconnectedException) { }
         }
-
         _dotnetObj?.Dispose();
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/BlazorUI/Bit.BlazorUI/Utils/BitPageVisibility.cs` around lines 50 - 59,
The Dispose method in BitPageVisibility is performing a fire-and-forget JS
interop call to "BitBlazorUI.PageVisibility.dispose" which hides teardown
failures and leaves the JS-side listener uncleared. Implement IAsyncDisposable
on the BitPageVisibility class instead of IDisposable, and replace the Dispose
method with a DisposeAsync method. In DisposeAsync, properly await the JS
cleanup operation using js.InvokeAsync instead of the fire-and-forget
js.InvokeVoid call, ensuring the _isInitialized flag is reset after the async
operation completes. This allows Blazor's DI lifecycle to properly handle the
asynchronous disposal of this scoped service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing FastInvoke js runtime extension methods usages from the BlazorUI

1 participant